﻿
Public Structure FileSize

#Region "Private Fields"
    Private _bytes As Long
#End Region

#Region "Constructors"
    ''' <summary>
    ''' Constructs the Object based on an input string representing the filesize.
    ''' </summary>
    ''' <param name="value">String representation of the File Size</param>
    Sub New(ByVal value As String)
        ParseFileSizeString(value)
    End Sub

    ''' <summary>
    ''' Constructs the Object based on a decimal value and a unit
    ''' </summary>
    ''' <param name="value">Value of the File Size</param>
    ''' <param name="unit">Unit of the File Size</param>
    Sub New(ByVal value As Decimal, ByVal unit As FileSizeUnit)
        Select Case unit
            Case FileSizeUnit.B
                _bytes = value
            Case FileSizeUnit.KB
                _bytes = value * 1024
            Case FileSizeUnit.MB
                _bytes = value * 1024 * 1024
            Case FileSizeUnit.GB
                _bytes = value * 1024 * 1024 * 1024
            Case FileSizeUnit.TB
                _bytes = value * 1024 * 1024 * 1024 * 1024
        End Select
    End Sub

    ''' <summary>
    ''' Constructs the Struture based of a baseline filesize in bytes
    ''' </summary>
    ''' <param name="bytes">The file size in bytes</param>
    Sub New(ByVal bytes As Long)
        _bytes = bytes
    End Sub
#End Region

#Region "Properties"
    ''' <summary>
    ''' FileSize converted to bytes
    ''' </summary>
    Public ReadOnly Property Bytes() As Long
        Get
            Return _bytes
        End Get
    End Property

    ''' <summary>
    ''' FileSize value converted to highest whole unit
    ''' </summary>
    Public ReadOnly Property Value() As Decimal
        Get
            Select Case Me.Unit
                Case FileSizeUnit.B
                    Return Me.Bytes
                Case FileSizeUnit.KB
                    Return Math.Round(Me.Bytes / 1024, 2)
                Case FileSizeUnit.MB
                    Return Math.Round(Me.Bytes / 1024 / 1024, 2)
                Case FileSizeUnit.GB
                    Return Math.Round(Me.Bytes / 1024 / 1024 / 1024, 2)
                Case Else
                    Return Math.Round(Me.Bytes / 1024 / 1024 / 1024 / 1024, 2)
            End Select
        End Get
    End Property

    ''' <summary>
    ''' Largest whole unit of measure
    ''' </summary>
    Public ReadOnly Property Unit() As FileSizeUnit
        Get
            Dim x As Integer = 0
            Dim value As Long = _bytes
            While value > 1000
                value = value / 1000
                x += 1
            End While
            Return x
        End Get
    End Property

    Public Property FileSizeUnit() As FileSizeUnit
        Get

        End Get
        Set(ByVal value As FileSizeUnit)

        End Set
    End Property

#End Region

#Region "Methods"
    ''' <summary>
    ''' Parses a file size string of it's value and unit.
    ''' </summary>
    Private Sub ParseFileSizeString(ByVal value As String)
        Dim val As Nullable(Of Decimal) = Nothing
        Dim unit As FileSizeUnit = Nothing
        Dim unitType As System.Type = GetType(FileSizeUnit)
        Dim enumValues As New List(Of Integer)([Enum].GetValues(unitType))
        enumValues.Reverse()

        For Each item As Integer In enumValues
            If value.Contains([Enum].GetName(unitType, item)) Then
                val = CDec(Trim(value.Replace([Enum].GetName(unitType, item), String.Empty)))
                unit = item
                Exit For
            End If
        Next

        If val Is Nothing Then
            Throw New InvalidFileSizeStringException()
        End If

        Select Case unit
            Case FileSizeUnit.B
                _bytes = Math.Ceiling(CDec(val))
            Case FileSizeUnit.KB
                _bytes = Math.Ceiling(CDec(val) * 1024)
            Case FileSizeUnit.MB
                _bytes = Math.Ceiling(CDec(val) * 1024 * 1024)
            Case FileSizeUnit.GB
                _bytes = Math.Ceiling(CDec(val) * 1024 * 1024 * 1024)
            Case FileSizeUnit.TB
                _bytes = Math.Ceiling(CDec(val) * 1024 * 1024 * 1024 * 1024)
        End Select
    End Sub

    Public Overrides Function ToString() As String
        Dim UnitType As System.Type = GetType(FileSizeUnit)
        Return String.Format("{0} {1}", Me.Value, [Enum].GetName(UnitType, Me.Unit))
    End Function

#Region "Operators"
    Public Shared Operator +(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As FileSize
        Return New FileSize(fs1.Bytes + fs2.Bytes)
    End Operator

    Public Shared Operator =(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As Boolean
        Return fs1.Bytes = fs2.Bytes
    End Operator

    Public Shared Operator <>(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As Boolean
        Return fs1.Bytes <> fs2.Bytes
    End Operator

    Public Shared Operator *(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As FileSize
        Return New FileSize(fs1.Bytes * fs2.Bytes)
    End Operator

    Public Shared Operator -(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As FileSize
        Return New FileSize(fs1.Bytes - fs2.Bytes)
    End Operator

    Public Shared Operator \(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As FileSize
        Return New FileSize(fs1.Bytes \ fs2.Bytes)
    End Operator

    Public Shared Operator <(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As Boolean
        Return fs1.Bytes < fs2.Bytes
    End Operator

    Public Shared Operator >(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As Boolean
        Return fs1.Bytes > fs2.Bytes
    End Operator

    Public Shared Operator >=(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As Boolean
        Return fs1.Bytes >= fs2.Bytes
    End Operator

    Public Shared Operator <=(ByVal fs1 As FileSize, ByVal fs2 As FileSize) As Boolean
        Return fs1.Bytes <= fs2.Bytes
    End Operator

    Public Shared Narrowing Operator CType(ByVal fs As FileSize) As String
        Return fs.ToString
    End Operator

    Public Shared Narrowing Operator CType(ByVal fs As FileSize) As Long
        Return fs.Bytes
    End Operator

    Public Shared Widening Operator CType(ByVal FileSizeString As String) As FileSize
        Return New FileSize(FileSizeString)
    End Operator
#End Region

#End Region
End Structure
